#include "RingQueue.hpp" //环形队列
#include <vector>
#include "Thread.hpp" //使用我自己模拟封装的C++线程对象
#include <string>
#include <unistd.h> //休眠时间sleep
#include <ctime>    //产生随机数
#include "Task.hpp"

using namespace ThreadModule;        // 打开自己封装的线程对象命名空间，std不打开，这样默认使用自己的
using ringqueue_t = RingQueue<Task>; // 这样的好处，就是阻塞对象类型，一改全改

void WaitAllThread(std::vector<Thread<ringqueue_t>> &threads)
{
    for (auto &thread : threads)
    {
        thread.Join(); // 回收改线程
    }
}

void Consumer(ringqueue_t &rq, std::string name)
{
    while (true)
    {
        sleep(2); // 消费的满一点，等生产满了，我才开始消费，此时我消费一个（历史数据），生产一个，消费一个，生产一个
        // 1. 消费任务
        Task t;
        rq.Pop(&t);
        std::cout << "Consumer handler task" << "[" << name << "]" << std::endl;
        // 处理任务
        t();
    }
}

void Productor(ringqueue_t &rq, std::string name)
{
    srand((unsigned)time(nullptr) ^ pthread_self()); // 设计随机数种子，用于产生数据
    int cnt = 10;
    while (true)
    {
        // 获取任务
        // 生产任务
        rq.Enqueue(Download);
        std::cout << "Productor:" << "[" << name << "]" << std::endl;
    }
}

// func就是模板实现，传Conumer还是传Productor都可以，实例化出对应的版本
void InitComm(std::vector<Thread<ringqueue_t>> *threads, int num, ringqueue_t &rq, func_t<ringqueue_t> func, const std::string &who)
{
    for (int i = 0; i < num; i++)
    {
        std::string name = "thread-" + std::to_string(i + 1) + who;
        threads->emplace_back(func, rq, name); // 直接构造线程对象，高效
        // 以下错误写法，因为stl数据结构也存在线程安全问题（该函数重入，不互斥），以下写法，会导致name乱码，所以解决以下问题，
        // 必须先创建所有对象，再来启动所有对象
        //  threads->back().Start();               // 并启动线程
    }
}

// void StartConsumer(std::vector<Thread<blockqueue_t>>{* threads, int num, blockqueue_t* bq) 错误写法，不能传指针，值拷贝
// 一旦往下传，下面引用至一个局部的指针变量，销毁，引用了非法空间会崩溃
// 以下正确写法，干脆传引用，创建出来的所有进程的数据和执行函数的参数都引用至同一个阻塞队列
void InitConsumer(std::vector<Thread<ringqueue_t>> *threads, int num, ringqueue_t &rq)
{
    InitComm(threads, num, rq, Consumer, "Consumer");
}
// StartConsumer和StartProductor有相似，只是构造线程对象时，传入要执行的函数不一样罢了，但函数都是void(T&)所以可以模板，调用同一个函数，传不同函数参数罢了
void InitProductor(std::vector<Thread<ringqueue_t>> *threads, int num, ringqueue_t &rq)
{
    InitComm(threads, num, rq, Productor, "Productor");
}

void StartAllThread(std::vector<Thread<ringqueue_t>> &threads)
{
    for (auto &thread : threads)
    {
        std::cout << "start: " << thread.name() << std::endl;
        thread.Start();
    }
}

int main()
{
    ringqueue_t *rq = new ringqueue_t(5);    // new一个阻塞队列，并指明大小
    std::vector<Thread<ringqueue_t>> threads; // 存放所有的线程对象
    // std::vector<Thread<ThreadData>> threads;

    // 先将所有生产者，消费者对象创建出来
    InitConsumer(&threads, 4, *rq);
    InitProductor(&threads, 5, *rq);
    // 2. 再来启动所有创建出来的对象，解决数据结构的线程安全问题
    StartAllThread(threads);
    // 回收所有线程
    WaitAllThread(threads);
    return 0;
}